解析模板字符串
// tpl(
// 'Hello, ${user.name} !' ,
// { user: { name: 'Hanmeimei' } }
// )
// 打印 Hello, Hanmeimei !
function tpl(template, data) {
return template.replace(/\$\{([^}]+)\}/g, (match, expr) => {
// 去除表达式首尾空格,并沿点号路径读取值
const value = expr.trim().split('.').reduce((obj, key) => obj?.[key], data);
// 如果取到的值是 undefined 或 null,返回空字符串,否则转为字符串
return value ?? '';
});
}
没问题,正则表达式一开始看确实像“天书”,但如果把它拆开来看,其实逻辑非常清晰。
这个正则表达式 /\$\{([^}]+)\}/g 的核心作用是:在一长串文本中,精准找到所有被 ${ 和 } 包裹的内容,并把中间的内容提取出来。
1. / 和 /g:正则的“外壳”和“指令”
/.../:这是 JavaScript 中正则表达式的固定写法,就像字符串用''包裹一样,正则表达式用/包裹。g:这是全局匹配标志(Global)。意思是“不要只找第一个,要把字符串里所有符合条件的都找出来”。
2. \$:匹配真实的 $ 符号
- 在正则表达式中,
$是一个有特殊含义的符号(代表字符串结尾)。 - 如果我们只想匹配普通的美元符号
$,就必须在前面加一个反斜杠\进行转义。所以\$就代表匹配真实的$字符。
3. \{:匹配真实的 { 符号
- 同理,
{在正则里也有特殊含义(用来表示数量,比如{3}表示重复3次)。 - 为了匹配真实的左花括号
{,也需要转义写成\{。 - 小结:
\$\{连在一起,就是告诉程序:“请在这里寻找真实的${这两个字符”。
4. ([^}]+):核心捕获组(提取内容)
这是整个正则最核心的部分,用来提取 ${} 里面的变量名。
(和):这叫捕获组。它的作用就像一个“套索”,把里面匹配到的内容单独拎出来。在 JS 的replace方法中,括号里的内容会作为回调函数的参数传给我们。[^}]:方括号[]表示字符集合,前面加上^表示**“取反”**。所以[^}]的意思是:匹配任何不是}的字符。这就保证了匹配过程不会跨越多个${}。+:表示“一次或多次”。- 小结:
([^}]+)连在一起,就是告诉程序:“请尽可能多地匹配字符,直到遇到}为止,并且把这些字符单独提取出来”。
5. \}:匹配真实的 } 符号
- 和前面的
\{一样,用来匹配真实的右花括号},作为占位符的结束标志。
解析模板字符串我的实现,共两个:
// tpl(
// 'Hello, ${user.name} !' ,
// { user: { name: 'Hanmeimei' } }
// )
function tpl(str, obj) {
if (str.length === 0) return ''
if (obj === null && typeof obj !== "object") return new Error('obj is not a object!!!')
const n = str.length
let start = 0
let end = n - 1
while (start < end) {
console.log(start, end)
if (start !== '{') start++
if (end !== '}') end--
}
console.log(start, end)
let path = str.slice(start, end + 1).split('.')
console.log(path, 'path')
let value = obj
for(let i = 0 ;i< path.length;i++){
value = value[path[i]]
}
let ans = str.slice(0, start - 1) + value + str.slice(end + 1, n)
return ans
}
tpl('Hello, ${user.name} !', { user: { name: 'Hanmeimei' } })
// =======第二个实现========
// tpl(
// 'Hello, ${user.name} !' ,
// { user: { name: 'Hanmeimei' } }
// )
function tpl(str, obj) {
if (str.length === 0) return ''
if (obj === null && typeof obj !== 'object')
return new Error('obj is not a object!!!')
const n = str.length
let start = str.indexOf('{')
let end = str.indexOf('}')
console.log(start, end)
let path = str.slice(start + 1, end).split('.')
console.log(path, 'path')
let value = obj
for (let i = 0; i < path.length; i++) {
value = value[path[i]]
}
console.log(value);
let ans = str.slice(0, start - 1) + value + str.slice(end + 1, n)
return ans
}
console.log(tpl('Hello, ${user.name} !', { user: { name: 'Hanmeimei' } }))
// Hello, Hanmeimei !